package com.coldlz.mmp.proxy;

import com.coldlz.mmp.proxy.handlerserver.ServerChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;


/**
 * 加载证书时可能会遇到
 * <p>Neither RSA, DSA nor EC worked
 * <p>algid parse error, not a sequence
 * <p>
 * 当遇到这种错误时，一般是证书格式错误
 * <p>PKCS1 的文件头格式 -----BEGIN RSA PRIVATE KEY-----</p>
 * <p>PKCS8 的文件头格式 -----BEGIN PRIVATE KEY-----</p>
 * 可使用一下命令来修改证书的格式：openssl pkcs8 -topk8 -inform PEM -in .keep.key -outform pem -nocrypt -out xxx8.key
 */
public class MMPServer {
    private static final Logger log = LoggerFactory.getLogger(MMPServer.class);
    /**
     * 全局唯一的 ServerBootstrap
     */
    private final ServerBootstrap serverBootstrap = new ServerBootstrap();
    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private final EventLoopGroup workerGroup = new NioEventLoopGroup(Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 4)));
    private final LinkedList<Runnable> callback = new LinkedList<>();
    private final ProxyConfig config;
    private volatile ChannelFuture serverFuture;
    private boolean started = false;


    private void init() {
        callback.addFirst(this::stop);
        callback.addLast(() -> {
            LogUtil.info(log,null,"开始关闭 bossGroup、workerGroup 线程组");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        });

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for (Runnable runnable : callback) {
                try {
                    runnable.run();
                } catch (Exception e) {
                    LogUtil.error(log,null,"关闭虚拟机异常", e);
                }
            }
        }));
    }


    public MMPServer(ProxyConfig config) {
        this.config = config;
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerChannelInitializer(config));
        init();
    }


    public synchronized void stop() {
        if (config.isSystemcProxyOpened()) {
            SystemProxySetting.resetSystemProxy();
            config.setSystemcProxyOpened(false);
        }
        ChannelFuture sf = serverFuture;
        if (sf != null) {
            ChannelFuture closeCF;
            try {
                closeCF = sf.channel().close().sync();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            serverFuture = null;
            started = false;
            if (closeCF.isSuccess()) {
                LogUtil.info(log,null,"Successfully closed service");
            } else {
                LogUtil.error(log,null,"Service shutdown failed", closeCF.cause());
            }
        }
    }


    public synchronized void start() {
        if (started) {
            throw new RuntimeException("请勿重复调用start函数");
        }
        int port = config.getPort();
        ChannelFuture sf = null;
        try {
            sf = serverBootstrap.bind(port).sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (sf.isSuccess()) {
            this.serverFuture = sf;
            this.started = true;
            if (config.isOpenSystemProxy()) {
                config.setSystemcProxyOpened(enableSystemProxy(config.getPort()));
            }

            // 添加关闭的监听器...
            sf.channel().closeFuture().addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    LogUtil.info(log,null,"Closed successfully");
                } else {
                    LogUtil.error(log,null,"Close failed", future.cause());
                }
            });
            log.info("======================================Successfully started on port {}======================================", port);
        } else {
            throw new RuntimeException("启动代理失败");
        }
    }

    public boolean started() {
        return started;
    }

    private static boolean enableSystemProxy(int port) {
        try {
            // 设置代理
            SystemProxySetting.enableSystemProxy(port);
            return true;
        } catch (Exception e) {
            log.error("开启代理失败", e);
            return false;
        }
    }
}
